今天,我們要來作Alpine Linux的initramfs bootstrapping。
在近代的大型distro中,多半都會善用early stage userspace,去進行比較複雜的rootfs初始化;各家的initramfs風格、打造方式可以差距到非常大,例如Arch Linux的工具是mkinitcpio
、Gentoo的則是Dracut
、Debian體系則是initramfs-tools
。
可是雖然風格迴異,這些initramfs的基本精神與流程皆是:從/proc/cmdline去拿到kernel boot arguements,然後爬看看有沒有initramfs要預處理的option,例如可能是nbd啦、或著initramfs特有的特技,例如Gentoo可以支援livenet
這個透過http去拿rootfs的方式;然後處理好rootfs mounting後,便會透過 switch_root 或著 pivot_root的方式去更換root node、然後啟動rootfs上的 init system ,例如 systemd
或著 openrc
等等。
然而,這邊一樣有個雞蛋問題,那就是這些initramfs的生成,通常是在一個native的環境底下,進行self-bootstrapping的;但是我們現在還沒有native的環境可以用,那怎麼辦呢?依筆者前年的作法,那便是去偷Alpine on ARMv7的來用XDDDDD
首先我們去 Alpine 官網下載 Generic ARM (ARMv7) 的 tar 包下來,解壓縮完,可以在boot資料夾底下看到 initramfs-lts
這個gzip壓縮過的cpio檔案。那麼,就來拆包吧:
mkdir extract
cd extract
cat ../initramfs-lts | gzip -d -c | cpio -idv
接著 tree 一下來看有哪些東西:
├── bin
│ ├── busybox
│ └── sh -> /bin/busybox
├── dev
├── etc
│ ├── apk
│ │ └── keys
│ │ ├── alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub
│ │ └── alpine-devel@lists.alpinelinux.org-58199dcc.rsa.pub
│ ├── fstab
│ ├── group
│ ├── mdev.conf
│ ├── modprobe.d
│ │ ├── aliases.conf
│ │ ├── blacklist.conf
│ │ ├── i386.conf
│ │ └── kms.conf
│ └── passwd
├── init
├── lib
│ ├── firmware
│ │ ├── ene-ub6250
│ │ │ ├── ms_init.bin
│ │ │ ├── msp_rdwr.bin
│ │ │ ├── ms_rdwr.bin
│ │ │ ├── sd_init1.bin
│ │ │ ├── sd_init2.bin
│ │ │ └── sd_rdwr.bin
│ │ └── qcom
│ ├── libcryptsetup.so.12 -> libcryptsetup.so.12.6.0
│ ├── libcryptsetup.so.12.6.0
│ ├── libdevmapper.so.1.02
│ ├── libkmod.so.2 -> libkmod.so.2.3.7
│ ├── libkmod.so.2.3.7
│ ├── libssl.so.1.1
│ ├── libuuid.so.1 -> libuuid.so.1.3.0
│ ├── libuuid.so.1.3.0
│ ├── libz.so.1 -> libz.so.1.2.11
│ ├── libz.so.1.2.11
│ ├── mdev
│ │ ├── dvbdev
│ │ ├── ide_links
│ │ ├── usbdev
│ │ ├── usbdisk_link
│ │ └── xvd_links
│ └── modules
│ └── 5.10.61-0-lts
│ ├── kernel
│ │ ├── arch
│ │ │ └── arm
│ │ │ └── lib
│ │ │ └── xor-neon.ko
│ │ ├── block
│ │ │ └── t10-pi.ko
......
此時,因為我們只要最基礎的骨架,firmware
跟modules
這部份是給wifi firmware還有一些driver的,就直接大刀闊斧的幹掉。緊接著開始去我們之前千辛萬苦打出來Alpine package repo撈出對應的骨幹來用:sudo apk -vv --allow-untrusted -X /path/to/packages/main/ -U --arch riscv32 --root $PWD/tmp_rootfs --initdb add alpine-base mkinitfs
緊接著就是非常手工藝地,把busybox、各式 shared object一個個換進去ARM initramfs裡面。
做完替換後,我們先把這份initramfs扔進一個loop dev image裡面,給之前buildroot的rv32 qemu virt試驗看看:
``
$ qemu-system-riscv32 -M virt -bios output/images/fw_jump.elf -kernel output/images/Image -append "rootwait root=/dev/vda rw init=/init" -drive file=/path/to/images.ext2,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -netdev user,id=net0 -device virtio-net-device,netdev=net0 -nographic
OpenSBI v0.9
/ __ \ / | _ _ |
| | | | __ ___ _ __ | ( | |) || |
| | | | '_ \ / _ \ '_ \ ___ | _ < | |
| || | |) | __/ | | |) | |) || |
_/| ./ _|| ||/|____/|
| |
|_|
Platform Name : riscv-virtio,qemu
Platform Features : timer,mfdeleg
Platform HART Count : 1
Firmware Base : 0x80000000
Firmware Size : 124 KB
Runtime SBI Version : 0.2
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x80000000-0x8001ffff ()
Domain0 Region01 : 0x00000000-0xffffffff (R,W,X)
Domain0 Next Address : 0x80400000
Domain0 Next Arg1 : 0x82200000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Boot HART ID : 0
Boot HART Domain : root
Boot HART ISA : rv32imafdcsu
Boot HART Features : scounteren,mcounteren,time
Boot HART PMP Count : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 32
Boot HART MHPM Count : 0
Boot HART MHPM Count : 0
Boot HART MIDELEG : 0x00000222
Boot HART MEDELEG : 0x0000b109
[ 0.000000] Linux version 5.10.7 (ruinland@ruinland-x1c) (riscv32-buildroot-linux-gnu-gcc.br_real (Buildroot 2021.08-235-g287a359090) 10.3.0, GNU ld (GNU Binutils) 2.36.1) #2 SMP Sun Sep 12 22:40:18 CST 2021
[ 0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80400000
[ 0.000000] efi: UEFI not found.
[ 0.000000] Zone ranges:
[ 0.000000] Normal [mem 0x0000000080400000-0x0000000087ffffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000080400000-0x0000000087ffffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000080400000-0x0000000087ffffff]
[ 0.000000] SBI specification v0.2 detected
[ 0.000000] SBI implementation ID=0x1 Version=0x9
[ 0.000000] SBI v0.2 TIME extension detected
[ 0.000000] SBI v0.2 IPI extension detected
[ 0.000000] SBI v0.2 RFENCE extension detected
[ 0.000000] SBI v0.2 HSM extension detected
...
[ 0.669946] EXT4-fs (vda): mounting ext2 file system using the ext4 subsystem
[ 0.679685] EXT4-fs (vda): warning: mounting unchecked fs, running e2fsck is recommended
[ 0.697676] EXT4-fs (vda): mounted filesystem without journal. Opts: (null)
[ 0.699126] ext2 filesystem being mounted at /root supports timestamps until 2038 (0x7fffffff)
[ 0.700403] VFS: Mounted root (ext2 filesystem) on device 254:0.
[ 0.706050] devtmpfs: mounted
[ 0.761174] Freeing unused kernel memory: 204K
[ 0.763022] Run /init as init process
[ 1.348089] Alpine Init 3.5.0-r0
Alpine Init 3.5.0-r0
[ 1.362497] Loading boot drivers...
雖然 `nlplug-findfs` 這隻Alpine initramfs拿來rootfs find and mount的helper會trigger runtime failure,但那可能是因為我們還沒有給他正確root dev的關係,我們下一章,就來debug吧~